import cv2 as cv
import numpy as np


# Sobel算子，用来计算图像灰度函数的近似梯度。根据像素点上下、左右邻点灰度加权差，在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用，提供较为精确的边缘方向信息，边缘定位精度不够高。当对精度要求不是很高时，是一种较为常用的边缘检测方法。
def sobel_demo(image):
    grad_x = cv.Sobel(image, cv.CV_32F, 1, 0)  # 对x求一阶导，dx参数表示x方向上的差分阶数，1或0 。
    grad_y = cv.Sobel(image, cv.CV_32F, 0, 1)  # 对y求一阶导，dy参数表示y方向上的差分阶数，1或0 。
    # 因为相互加减，则运算后的数值可能超过255，因此不用uint8而用float32
    gradx = cv.convertScaleAbs(grad_x)  # 用convertScaleAbs()函数将其转回原来的uint8形式
    grady = cv.convertScaleAbs(grad_y)
    cv.imshow("gradient_x", gradx)  # x方向上的梯度
    cv.imshow("gradient_y", grady)  # y方向上的梯度
    gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)  # 图片融合
    # src1参数表示需要加权的第一个输入数组。
    # alpha参数表示第一个数组的权重。
    # src2参数表示第二个输入数组，它和第一个数组拥有相同的尺寸和通道数。
    # beta参数表示第二个数组的权重。
    # gamma参数表示一个加到权重总和上的标量值。
    cv.imshow("gradient", gradxy)


# Scharr算子(Sobel算子的增强版，效果更突出,但是对噪声和敏感)
def scharr_demo(image):
    grad_x = cv.Scharr(image, cv.CV_32F, 1, 0)  # 对x求一阶导
    grad_y = cv.Scharr(image, cv.CV_32F, 0, 1)  # 对y求一阶导
    gradx = cv.convertScaleAbs(grad_x)  # 用convertScaleAbs()函数将其转回原来的uint8形式
    grady = cv.convertScaleAbs(grad_y)
    cv.imshow("gradient_x", gradx)  # x方向上的梯度
    cv.imshow("gradient_y", grady)  # y方向上的梯度
    gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
    cv.imshow("gradient", gradxy)


# 拉普拉斯算子
def laplace_demo(image):
    # dst = cv.Laplacian(image, cv.CV_32F)  # 直接引用API的方法，也可以自己写卷积核
    kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
    dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
    lpls = cv.convertScaleAbs(dst)
    cv.imshow("Laplace_demo", lpls)


src = cv.imread('imgs/test008.jpg')
cv.imshow('input_image', src)
laplace_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()